package helpdiabetesj2me;

import java.util.Date;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreNotOpenException;

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author Johan VM Windows 7
 */
public class ActiveInsulinManagement {
    
    static private RecordStore activeInsulinRS;
    static final private int MAX_RECORD_LENGTH = 100;
    /**
     * how long to keep insulin events in recordstore, actually they will be kept twice that long
     */
    static public long MAX_TIME_TO_KEEP_ACTIVE_INSULIN_EVENT_IN_MS = 18000000;
    
    /**
     * each element is a number of minutes, after which the corresponding percentage is reached
     */
    static private int[] insulinProfile = new int[] {
        0, //100 first value must always be 0 100
        30, //95 ==> my value = 30
        30, //90 since the amount of minutes is equal to the first, ignore it, my value = 30
        30, //85 ==> my value = 30
        30, //80
        30, //75
        60, //70
        60, //65
        60, //60
        60, //55
        60, //50
        60, //45
        60, //40
        90, //35
        90, //30
        90, //25
        90, //20
        90, //15
        120, //10
        180, //5
        240//0
    };

    public ActiveInsulinManagement() {
    }
    
    /**
     * return activeinsulin, for a bolus of amount units, given activeTimeInSeconds seconds ago,
     * if it's a square wave then durationOfTheEventInMinutes is > 0
     */
    static public float getActiveInsulin(int activeTimeInSeconds, float amount, int durationOfTheEventInMinutes) {
        //if it's a square wave then call getActiveInsulinr recursively
        if (durationOfTheEventInMinutes> 0) {
            float returnValue = 0;
            //how mucht of the amount is already given ?
            float givenAmount;
            float durationDuringWhichAmountIsGivenInSeconds;
            if (activeTimeInSeconds > durationOfTheEventInMinutes * 60) {
                givenAmount = amount;
                durationDuringWhichAmountIsGivenInSeconds = durationOfTheEventInMinutes * 60;
            } else {
                givenAmount = amount * activeTimeInSeconds/(durationOfTheEventInMinutes * 60);
                durationDuringWhichAmountIsGivenInSeconds = activeTimeInSeconds;
            }
            //now we assume the givenAmount is given in little shots of 0.1
            int amountOfShots = (int)(givenAmount * 10);
            float intervalBetweenTwoShotsInSeconds = durationDuringWhichAmountIsGivenInSeconds/(float)amountOfShots;
            //so ther's amountofshots little bolusses of 0.1 units of insulin
            for (int shotCounter = 0;shotCounter < amountOfShots;shotCounter++) {
                returnValue += getActiveInsulin(activeTimeInSeconds - (int)(shotCounter * intervalBetweenTwoShotsInSeconds), (float)0.1, 0);
            }
            return returnValue;
        }
        //we need to go from which percentage to which percentage, and from how many minutes to how many minutes
        int fromIndex;
        int toIndex;
        
        //to start with , if duration is >= duration at which percentage = 0, then return 0
        if (activeTimeInSeconds >= insulinProfile[insulinProfile.length - 1] * 60) {
            return 0;
        }
        
        //goal here is to check which two elements in the array we need to use
        //for example, 
        //  assume duration = 25 minutes = 1500 seconds, then fromindex should be 0, toindex should be 1
        //  assume duration = 45 minutes = 2700 seconds, then fromindex should be 1, toindex should be 6
        //  assume duration = 60 minutes = 3600 seconds, then fromindex should be 6, toindex should be 13
        //  assume duration = 220 minutes = 13200 seconds, then fromindex should be 20, toindex should be 20
        fromIndex = 0;
        int arrayCounter = 1;
        toIndex = arrayCounter;
        while (arrayCounter < insulinProfile.length) {
           //toIndex = arrayCounter;
           if (insulinProfile[arrayCounter] * 60 < activeTimeInSeconds) {
               if (insulinProfile[arrayCounter] > insulinProfile[arrayCounter - 1])
                       fromIndex = arrayCounter;
               if (arrayCounter < insulinProfile.length - 1) {
                   toIndex = arrayCounter + 1;
               }
               else
                   toIndex = arrayCounter;
           } else {
               if (insulinProfile[arrayCounter] * 60 > activeTimeInSeconds) {
                   //we can stop
                   arrayCounter = insulinProfile.length;
               } else
                   toIndex = arrayCounter;
           }
        arrayCounter++;
        }
        
        //now calculate how much percentage is still there
        float startPercentage = 100 - fromIndex * 5;
        float startTimeInSeconds = insulinProfile[fromIndex] * 60;
        float endPercentage = 100 - toIndex * 5;
        float endTimeInSeconds = insulinProfile[toIndex] * 60;
        float actualPercentage = 
                startPercentage 
                - 
                (startPercentage - endPercentage)
                /
                (endTimeInSeconds - startTimeInSeconds) * (activeTimeInSeconds - startTimeInSeconds);
        
        //now calculate returnvalue
        
        return amount * actualPercentage / 100;
    }
    
    /**
     * returns all active insulin
     */
   static public float getTotalActiveInsulin() {
        ActiveInsulinEvent[] activeInsulinEvents = getListOfActiveInsulinEvents();
        float activeInsulinAmount = 0;
        for (int i = 0 ;i < activeInsulinEvents.length; i++) {
            activeInsulinAmount = 
                    activeInsulinAmount 
                    + 
                    getActiveInsulin
                        (
                                (int)((new Date()).getTime() - (activeInsulinEvents[i]).getDate().getTime())/1000
                        ,
                               ((activeInsulinEvents[i]).getAmount())
                        ,
                               activeInsulinEvents[i].getDurationInMinutes()
                        );
        }
        return activeInsulinAmount;
    }
    
    static public ActiveInsulinEvent getActiveInsulinEvent(int recordId) {
        byte[] record = null;
        try {
            record = getActiveInsulRecordStore().getRecord(recordId);
        } catch (RecordStoreException ex) {
            ex.printStackTrace();
        }
        String[] splitted = Utilities.split(new String(record).substring(0, record.length),";");
        long time = Long.parseLong(splitted[0]);
        float amount = java.lang.Float.parseFloat(splitted[1]);
        int duration = Integer.parseInt(splitted[2]);
        return
                        (new ActiveInsulinEventBuilder())
                                .setAmount(amount)
                                .setDate(new Date(time))
                                .setRecordStoreId(recordId)
                                .setDurationInMinutes(duration)
                                .createActiveInsulinEvent();
    }
    static private RecordStore getActiveInsulRecordStore() {
        if (activeInsulinRS != null)
            return activeInsulinRS;
        try {
            activeInsulinRS = RecordStore.openRecordStore("activeinsulin", true);
            //let's see if a cleanup needs to occur
            byte[] retrievedRecord = new byte[MAX_RECORD_LENGTH];
            RecordEnumeration renum = activeInsulinRS.enumerateRecords(null, null, false);
            while (renum.hasNextElement()) {
                int i = renum.nextRecordId();
                int length = activeInsulinRS.getRecord(i, retrievedRecord, 0);
                String[] splitted = Utilities.split(new String(retrievedRecord).substring(0, length),";");
                long time = Long.parseLong(splitted[0]);
                if ((new Date()).getTime() - time > MAX_TIME_TO_KEEP_ACTIVE_INSULIN_EVENT_IN_MS) {
                    activeInsulinRS.deleteRecord(i);
                }
            }
        }   catch (RecordStoreException ex) {
            ex.printStackTrace();
        } 
        return activeInsulinRS;
    }
    
    /**
     * 
     * @return -1 if failed, otherwise the number of the record in the recordstore<br>
     * if recordId = -1 then it's a new record
     */
    static public int addOrUpdateActiveInsulinEvent(ActiveInsulinEvent activeInsulinEvent, int recordId) {
        byte[] record = (activeInsulinEvent.getDate().getTime() + ";" + activeInsulinEvent.getAmount() + ";" + activeInsulinEvent.getDurationInMinutes()).getBytes();
        int returnValue = -1;
        try {
            if (recordId == -1)
                returnValue = getActiveInsulRecordStore().addRecord(record, 0, record.length);
            else {
                getActiveInsulRecordStore().setRecord(recordId, record, 0, record.length);
                returnValue = recordId;
            } 
        } catch (RecordStoreException ex) {
            ex.printStackTrace();
        }
        return returnValue;
    }
    
    /**
     * returns active insulin events, should always return an array, if there's no events then an array with size <br>
     * check anyway if it's null before using it
     */
    static public ActiveInsulinEvent[] getListOfActiveInsulinEvents() {
        ActiveInsulinEvent[] returnValue = null;
        try {
            returnValue = new ActiveInsulinEvent[getActiveInsulRecordStore().getNumRecords()];
            byte[] retrievedRecord = new byte[MAX_RECORD_LENGTH];
            RecordEnumeration renum = activeInsulinRS.enumerateRecords(null, null, false);
            int j = 0;
            while (renum.hasNextElement()) {
                int recordId = renum.nextRecordId();
                int length = getActiveInsulRecordStore().getRecord(recordId, retrievedRecord, 0);
                String[] splitted = Utilities.split(new String(retrievedRecord).substring(0, length),";");
                long time = Long.parseLong(splitted[0]);
                float amount =   java.lang.Float.parseFloat(splitted[1]);
                int durationInMinutes = Integer.parseInt(splitted[2]);
                returnValue[j++] =
                        (new ActiveInsulinEventBuilder())
                                .setAmount(amount)
                                .setDate(new Date(time))
                                .setRecordStoreId(recordId)
                                .setDurationInMinutes(durationInMinutes)
                                .createActiveInsulinEvent();
            }
        } catch (RecordStoreNotOpenException ex) {
            ex.printStackTrace();
        } catch (RecordStoreException ex2) {
            ex2.printStackTrace();
        }
        //let's sort them - simple binary sort
        boolean sorted = false;
        while (!sorted && returnValue.length > 1) {
            sorted = true;
            int i = 0;
            do {
                if (returnValue[i].getDate().getTime() < returnValue[i + 1].getDate().getTime()) {
                    sorted = false;
                    ActiveInsulinEvent temp = returnValue[i];
                    returnValue[i] = returnValue[i + 1];
                    returnValue[i + 1] = temp;
                }
                i++;
            } while (i < returnValue.length - 1);
        }
        return returnValue;
    }
}
